package com.dzt.tool.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.dzt.tool.dao.interfaces.BaseDao;
import com.dzt.tool.model.dto.Pager;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class BaseDaoImpl<T> implements BaseDao<T> {
	private Class<T> clz;

	@Autowired
	@Qualifier("sessionFactory")
	private SessionFactory sessionFactory;

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	private Class<T> getClz() {
		if (clz == null) {
			clz = ((Class<T>) (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]));
		}
		return clz;
	}

	@Override
	public T addOrUpdate(T t) {
		this.getSession().saveOrUpdate(t);
		return t;
	}

	@Override
	public T add(T t) {
		this.getSession().save(t);
		return t;
	}

	@Override
	public void update(T t) {
		this.getSession().update(t);
	}

	@Override
	public void delete(long id) {
		T t = this.load(id);
		this.getSession().delete(t);
	}

	@Override
	public void delete(String id) {
		T t = this.load(id);
		this.getSession().delete(t);
	}

	@Override
	public void delete(T t) {
		this.getSession().delete(t);
	}

	@Override
	public T load(long id) {
		return (T) this.getSession().get(getClz(), id);
	}

	@Override
	public T load(String id) {
		return (T) this.getSession().get(getClz(), id);
	}

	private String initSort(String hql) {
		String order = "";
		String sort = "";
		if (StringUtils.isNotEmpty(sort)) {
			hql += " order by " + sort;
			if (!"desc".equals(order)) {
				hql += " asc ";
			} else {
				hql += " desc ";
			}
		}
		return hql;
	}

	private void setAliasParameter(Query query, Map<String, Object> alias) {
		if (alias != null) {
			Set<String> keys = alias.keySet();
			for (String key : keys) {
				Object val = alias.get(key);
				if (val instanceof Collection) {
					query.setParameterList(key, (Collection) val);
				} else {
					query.setParameter(key, val);
				}
			}
		}
	}

	private void setParameter(Query query, Object[] args) {
		if (args != null && args.length > 0) {
			int index = 0;
			for (Object arg : args) {
				query.setParameter(index++, arg);
			}
		}
	}

	@Override
	public List<T> list(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		Query query = this.getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.list();
	}

	@Override
	public List<T> list(String hql, Object[] args) {
		return this.list(hql, args, null);
	}

	@Override
	public List<T> list(String hql, Object obj) {
		return this.list(hql, new Object[] { obj });
	}

	@Override
	public List<T> list(String hql) {
		return this.list(hql, null);
	}

	@Override
	public List<T> listByAlias(String hql, Map<String, Object> alias) {
		return this.list(hql, null, alias);
	}

	private String getCountHql(String hql, boolean isHql) {
		String e = hql.substring(hql.indexOf("from"));
		String c = "select count(*) " + e;
		if (isHql) {
			c.replaceAll("fetch", "");
		}
		return c;
	}

	private void setPagers(Query query, Pager pages) {
		Integer pageSize = 10;
		Integer pageOffset = 1;
		if (pageOffset == null || pageOffset < 0) {
			pageOffset = 0;
		}
		if (pageSize == null || pageSize < 0) {
			pageSize = 15;
		}
		pages.setPageOffset(pageOffset);
		pages.setPageSize(pageSize);
		query.setFirstResult(pageOffset).setMaxResults(pageSize);
	}

	@Override
	public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		String cq = getCountHql(hql, true);
		Query cquery = getSession().createQuery(cq);
		Query query = getSession().createQuery(hql);
		// 设置别名参数
		setAliasParameter(query, alias);
		setAliasParameter(cquery, alias);
		// 设置参数
		setParameter(query, args);
		setParameter(cquery, args);
		Pager<T> pages = new Pager<T>();
		setPagers(query, pages);
		List<T> datas = query.list();
		pages.setDatas(datas);
		long total = (Long) cquery.uniqueResult();
		pages.setTotalRecord(total);
		return pages;
	}

	@Override
	public Pager<T> find(String hql, Object[] args) {
		return this.find(hql, args, null);
	}

	@Override
	public Pager<T> find(String hql, Object obj) {
		return this.find(hql, new Object[] { obj });
	}

	@Override
	public Pager<T> find(String hql) {
		return this.find(hql, null);
	}

	@Override
	public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
		return this.find(hql, null, alias);
	}

	@Override
	public Object queryByHql(String hql, Object[] args, Map<String, Object> alias) {
		Query query = this.getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.uniqueResult();
	}

	@Override
	public Object queryByHql(String hql, Object[] args) {
		return this.queryByHql(hql, args, null);
	}

	@Override
	public Object queryByHql(String hql, Object arg) {
		return this.queryByHql(hql, new Object[] { arg });
	}

	@Override
	public Object queryByHql(String hql) {
		return this.queryByHql(hql, null);
	}

	@Override
	public Object queryByHqlByAlias(String hql, Map<String, Object> alias) {
		return this.queryByHql(hql, null, alias);
	}

	@Override
	public void executeByHql(String hql, Object[] args) {
		Query query = this.getSession().createQuery(hql);
		this.setParameter(query, args);
		query.executeUpdate();
	}

	@Override
	public void executeByHql(String hql, Object arg) {
		this.executeByHql(hql, new Object[] { arg });
	}

	@Override
	public void executeByHql(String hql) {
		this.executeByHql(hql, null);
	}

	@Override
	public List<Object> listByObj(String hql, Object[] args) {
		Query query = this.getSession().createQuery(hql);
		this.setParameter(query, args);
		return query.list();
	}

	@Override
	public List<Object> listByObj(String hql) {
		return this.listByObj(hql, null);
	}

	@Override
	public List<Object> listByObj(String hql, Object obj) {
		return this.listByObj(hql, new Object[] { obj });
	}

	@Override
	public Pager<Object> findByObj(String hql, Object[] args) {
		hql = initSort(hql);
		String cq = getCountHql(hql, true);
		Query cquery = getSession().createQuery(cq);
		Query query = getSession().createQuery(hql);
		setParameter(query, args);
		setParameter(cquery, args);
		Pager<Object> pages = new Pager<Object>();
		setPagers(query, pages);
		List<Object> datas = query.list();
		pages.setDatas(datas);
		long total = (Long) cquery.uniqueResult();
		pages.setTotalRecord(total);
		return pages;
	}

	@Override
	public Pager<Object> findByObj(String hql, Object obj) {
		return this.findByObj(hql, new Object[] { obj });
	}

	@Override
	public Pager<Object> findByObj(String hql) {
		return this.findByObj(hql, null);
	}

	@Override
	public <N extends Object> List<N> listBySql(String sql, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		sql = initSort(sql);
		SQLQuery sq = getSession().createSQLQuery(sql);
		setAliasParameter(sq, alias);
		setParameter(sq, args);
		if (hasEntity) {
			sq.addEntity(clz);
		} else {
			sq.setResultTransformer(Transformers.aliasToBean(clz));
		}
		return sq.list();
	}

	@Override
	public <N> List<N> listBySql(String sql, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, args, null, clz, hasEntity);
	}

	@Override
	public <N> List<N> listBySql(String sql, Object arg, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, new Object[] { arg }, clz, hasEntity);
	}

	@Override
	public <N> List<N> listBySql(String sql, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, null, clz, hasEntity);
	}

	@Override
	public <N> List<N> listByAliasSql(String sql, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, null, alias, clz, hasEntity);
	}

	@Override
	public <N extends Object> Pager<N> findBySql(String sql, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		sql = initSort(sql);
		String cq = getCountHql(sql, false);
		SQLQuery sq = getSession().createSQLQuery(sql);
		SQLQuery cquery = getSession().createSQLQuery(cq);
		setAliasParameter(sq, alias);
		setAliasParameter(cquery, alias);
		setParameter(sq, args);
		setParameter(cquery, args);
		Pager<N> pages = new Pager<N>();
		setPagers(sq, pages);
		if (hasEntity) {
			sq.addEntity(clz);
		} else {
			sq.setResultTransformer(Transformers.aliasToBean(clz));
		}
		List<N> datas = sq.list();
		pages.setDatas(datas);
		long total = ((BigInteger) cquery.uniqueResult()).longValue();
		pages.setTotalRecord(total);
		return pages;
	}

	@Override
	public <N> Pager<N> findBySql(String sql, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, args, null, clz, hasEntity);
	}

	@Override
	public <N> Pager<N> findBySql(String sql, Object arg, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, new Object[] { arg }, clz, hasEntity);
	}

	@Override
	public <N> Pager<N> findBySql(String sql, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, null, clz, hasEntity);
	}

	@Override
	public <N> Pager<N> findByAliasSql(String sql, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, null, alias, clz, hasEntity);
	}

	@Override
	public List<T> findByEasyUI(String hql, int page, int rows, Object... param) {
		Query query = this.getSession().createQuery(hql);
		if (param != null && param.length > 0) {
			for (int i = 0; i < param.length; i++) {
				query.setParameter(i, param[i]);
			}
		}
		return query.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}

	@Override
	public List<Object> findApp(String hql, int page, int rows, Object... param) {
		Query query = this.getSession().createQuery(hql);
		if (param != null && param.length > 0) {
			for (int i = 0; i < param.length; i++) {
				query.setParameter(i, param[i]);
			}
		}
		return query.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}

	@Override
	public List<Object> listBySql(String sql, Object[] args) {
		sql = initSort(sql);
		SQLQuery sq = getSession().createSQLQuery(sql);
		setParameter(sq, args);
		return sq.list();
	}
}